package com.hp.bon.jdbc;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * URL: jdbc:hp:disaster:<Disaster DSN><Real DSN>?debug=Y
 * Tables: Table1|Table2|...
 * For Example:
 * jdbc:hp:disaster:sc_db:sc_db_disaster?debug=Y * 
 * 
 * 	<!-- 进行数据库复制时，sessionFactory需要引用的dataSource是disasterDataSource -->
	<bean id="disasterJdbcDriver" class="com.hp.bon.jdbc.DisasterJdbcDriverWithSpring"
		depends-on="dataSource">
		<property name="localDS">
			<ref bean="dataSource" />
		</property>
		<property name="disasterTables" value="T_NODE;t_sgw_channel" />
	</bean>
 */
public class DisasterJdbcDriverWithSpring implements Driver
{
	private static final String URL_PREFIX = "jdbc:hp:disaster:";
	static final Logger LOG = Logger.getLogger(DisasterJdbcDriverWithSpring.class.getPackage().getName());

	public boolean acceptsURL(String url) throws SQLException
	{
		if (url == null) return false;
		if (!url.startsWith(URL_PREFIX)) return false;
		return true;
	}

	public Connection connect(String url, Properties info) throws SQLException
	{
		if (!acceptsURL(url)) return null;//throw new SQLException("Invalid URL:" + url);
		try
		{
			//Properties
			_props.clear();
			if (info != null) _props.putAll(info);
			StringTokenizer st = new StringTokenizer(url.substring(URL_PREFIX.length()), ":?");
			//Disaster-dsn
			if (!st.hasMoreTokens())
				throw new SQLException("Invalid URL, must like jdbc:hp:disaster:<Disaster DSN>:<Real DSN>");
			String ddDSN = st.nextToken();
			//real-dsn
			if (!st.hasMoreTokens())
				throw new SQLException("Invalid URL, must like jdbc:hp:disaster:<Disaster DSN>:<Real DSN>");
			String s1 = st.nextToken();
//			DataSource ds1 = _cpm.getDataSource(s1);
			DataSource ds1 = localDS;
			if (ds1 == null) throw new SQLException("Undefined DSN " + s1);
			//tables
			s1 = disasterTables;
//			s1 = _config.getParameter(ddDSN, "DisasterTables");
//			if (!st.hasMoreTokens()) throw new SQLException("Invalid URL, must like jdbc:hp:disaster:<DSN>:<Tables>");
//			s1 = st.nextToken();
			StringTokenizer st2 = new StringTokenizer(s1, ",;\n |");
			Collection<String> tables = new HashSet<String>();
			while (st2.hasMoreTokens()) tables.add(st2.nextToken().trim().toUpperCase());
			//parameters
			String urlParas = st.hasMoreTokens() ? st.nextToken() : null;
			//
			if (urlParas != null)
			{
				st = new StringTokenizer(urlParas, "&");
				while (st.hasMoreTokens())
				{
					String s = st.nextToken();
					int kk = s.indexOf('=');
					_props.put(s.substring(0, kk).toLowerCase(), s.substring(kk + 1));
				}
			}
			parseProps();
			LOG.log(Level.FINEST, "Disaster implemented data source " + ds1 + " from configuration, Auto replication is "+_autoRep);
			for (String s : tables)
			{
				LOG.log(Level.FINEST, "Disaster tables:" + s);
			}
			if(_autoRep)
			{
				System.out.println("Disaster implemented data source " + ds1 + " from configuration, Auto replication is "+_autoRep);
			}

			Connection conn1 = ds1.getConnection();
			Connection f = (Connection) java.lang.reflect.Proxy.newProxyInstance(getClass().getClassLoader(),
					new Class[]{Connection.class},
					new DisasterConnection(conn1, tables));
			//
			if (!_isTableCreated) tryCreateTable(conn1);
			return f;
		}
		catch (SQLException e)
		{
			e.printStackTrace(System.err);
			if (_out != null) e.printStackTrace(_out);
			throw e;
		}
		catch (Exception e)
		{
			e.printStackTrace(System.err);
			if (_out != null) e.printStackTrace(_out);
			throw new SQLException("Exception while connect to " + url + ", Exception:" + e.getMessage());
		}
	}
	private static DataSource localDS; 
	private static String disasterTables;
	private void parseProps()
	{
		if (_props.get("debug") != null)
		{
			_out = new LoggerWrapperPrintWriter(LOG);
		}
		String s = _props.getProperty("autorep");
		_autoRep = ((s != null) && (s.toUpperCase().equals("Y")));
	}

	private void tryCreateTable(Connection conn)
	{
		String sql = "SELECT COUNT(*) FROM " + TABLE_LOG;
		Statement st = null;
		ResultSet rs = null;
		try
		{
			st = conn.createStatement();
			rs = st.executeQuery(sql);
			_isTableCreated = true;
		}
		catch (SQLException e)
		{
			_isTableCreated = false;
		}
		finally
		{
			try
			{
				if (st != null) st.close();
				if (rs != null) rs.close();
			}
			catch (SQLException e)
			{
				e.printStackTrace(System.err);
			}
		}
		if (_isTableCreated) return;
		try
		{
			sql = TABLE_LOG_DDL;
			LOG.info("Disaster log table is not exist, try to create disaster table by sql " + sql);
			st = conn.createStatement();
			st.executeUpdate(sql);
			_isTableCreated = true;
		}
		catch (SQLException e)
		{
			LOG.log(Level.SEVERE, "Create disaster log table error!", e);
		}
		finally
		{
			try
			{
				if (st != null) st.close();
			}
			catch (SQLException e)
			{
				e.printStackTrace(System.err);
			}
		}

	}

	public int getMajorVersion()
	{
		return 1;
	}

	public int getMinorVersion()
	{
		return 0;
	}

	public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException
	{
		return null;
	}

	public boolean jdbcCompliant()
	{
		return true;
	}

    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }

    private Properties _props = new Properties();

	public DisasterJdbcDriverWithSpring()
	{
		if (DriverManager.getLogWriter() != null)
			_out = new PrintWriter(DriverManager.getLogWriter(), true);
	}

	private PrintWriter _out;

	static
	{
		try
		{
			DriverManager.registerDriver(new DisasterJdbcDriverWithSpring());
		}
		catch (SQLException e)
		{
			e.printStackTrace(System.err);
		}
	}


//	private ConnectionPoolManager _cpm = ConnectionPoolManager.getInstance();
//	static private XmlConfig _config = DeamonApplicationHelper.getInstance().getConfig();
	static private boolean _isTableCreated = false;
	static final String TABLE_LOG = "DIASTER$JDBC_LOG";
	static final private String TABLE_LOG_DDL = "create table " + TABLE_LOG + "  (" +
			"   log_id               VARCHAR(128)                    not null," +
			"   transaction_id       VARCHAR(128)                    not null," +
			"   create_time          TIMESTAMP                      default SYSDATE not null," +
			"   sql                  VARCHAR(4000)                  not null," +
			"   sql_value            VARCHAR(4000)," +
			"   effected_rows        NUMBER(9) not null," +
			"   last_handle_time     TIMESTAMP," +
			"   last_handle_error    VARCHAR(4000)," +
			"   last_try_count       NUMBER(9)                      default 0 not null," +
			"   state                CHAR(1)                        default 'A'," +
			"   constraint PK_DIASTER$JDBC_LOG primary key (log_id)" +
			")";

	static private boolean _autoRep;

	static void tryReplication()
	{
		if(!_autoRep) return;
		//TODO do replication if autoRep=Y

	}

	public DataSource getLocalDS() {
		return localDS;
	}

	public void setLocalDS(DataSource localDS) {
		DisasterJdbcDriverWithSpring.localDS = localDS;
	}

	public String getDisasterTables() {
		return disasterTables;
	}

	public void setDisasterTables(String disasterTables) {
		DisasterJdbcDriverWithSpring.disasterTables = disasterTables;
	}
}
